/*
 * Copyright 2010 Thedwick, LLC

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

 */
package com.thedwick.jdbcGrabber.analysis;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.thedwick.jdbcGrabber.analysis.parser.CapturedStatement;
import com.thedwick.jdbcGrabber.analysis.parser.CapturedTable;
import com.thedwick.jdbcGrabber.analysis.parser.CapturedTrace;
import com.thedwick.jdbcGrabber.analysis.parser.CapturedTraceElement;
import com.thedwick.jdbcGrabber.analysis.parser.FileBasedResultsParser;
import com.thedwick.jdbcGrabber.analysis.parser.ResultsParser;


public class GraphvizDotGraph {
	/**
	 * @param args
	 * @throws IOException 
	 */
	public static void main(String[] args) throws IOException {
		if (args.length < 2){
			System.err.println("Usage: GraphvizDotGraph <filepath> <packageprefix> <sinkname1> <sinkname2> ...." +
					"\nWhere" +
					"\n<filepath> is the path to all the captured data files produced by a SummarizingSink and a FileSummaryStore, for example '/tmp'" +
					"\n<packageprefix> is the prefix of the Java packages that belong to your organization, for example 'com.yahoo.'" +
					"\n<sinkname> is one or more names, as specified by the 'sink.name' property in your connection's properties file");
			System.exit(1);
		}
		String path = args[0];
		String packageName = args[1];
		List<String> sinks = new ArrayList<String>(args.length - 3);
		for (int i=2 ; i < args.length; i++){
			sinks.add(args[i]);
		}
		GraphvizDotGraph summary = new GraphvizDotGraph();
		summary.summarize(path, packageName, sinks);

	}

	
	
	private void summarize(String path, String packageName, List<String> sinks) throws IOException {
		int id = 1;
		out("digraph G {");

		Map<String, Integer> classIds = new HashMap<String, Integer>();
		Map<String, Integer> tableIds = new HashMap<String, Integer>();
		Set<String> existingEdges = new HashSet<String>();
		StringBuilder javaSubGraph = new StringBuilder();
		for (String sink : sinks){
			out("\tsubgraph cluster_" + id++ + "{");
			ResultsParser parser = new FileBasedResultsParser(sink,path);
			Set<CapturedStatement> statements = parser.parse();
			for (CapturedStatement statement : statements){
				StringBuilder builder = new StringBuilder();
				Set<Integer> calledTables = new HashSet<Integer>();
				for (CapturedTable table : statement.getTables()){
					builder.append("\t\t");
					int tableId = -1;
					if (tableIds.containsKey(table.getName())){
						tableId = tableIds.get(table.getName());
					} else {
						tableId = id++;
						tableIds.put(table.getName(), tableId);
					}
					builder.append(String.valueOf(tableId));
					calledTables.add(tableId);
					builder.append(" [label=\"");
					builder.append(table.getName());
					builder.append("\"];\n");
				}
				String abbrevSql = abbreviate(statement.getSql());
				int sqlId = id++;
				builder.append("\t\t");
				builder.append(String.valueOf(sqlId));
				builder.append(" [label=\"");
				builder.append(abbrevSql);
				builder.append("\"];\n");
				for (Integer table : calledTables){
					int start = sqlId;
					int end = table;
					builder.append(start);
					builder.append(" -> ");
					builder.append(end);
					builder.append(" [label=\"");
					builder.append("");
					builder.append("\"];\n");

				}
				for (CapturedTrace trace : statement.getTraces()){
					List<CapturedTraceElement> elements = trace.getElements();
					int previousClass = sqlId;
					String previousClassName = "";
					String previousMethod = trace.getCount() + " times";
					int currentClass = -1;
					int size = elements.size();
					for (int i=0 ; i<size ; i++){
						CapturedTraceElement element = elements.get(i);
						//output only the first and last elements plus any that belong to this organization
						// also filter out calls from one class back to itself
						if ((i == 0 || i == (size-1) || element.getClassName().startsWith(packageName)) &&
								! previousClassName.equals(element.getClassName())){
							if (classIds.containsKey(element.getClassName())){
								currentClass = classIds.get(element.getClassName());
							} else {
								currentClass = id++;
								classIds.put(element.getClassName(), currentClass);
								javaSubGraph.append("\t\t");
								javaSubGraph.append(String.valueOf(currentClass));
								javaSubGraph.append(" [label=\"");
								javaSubGraph.append(element.getClassName());
								javaSubGraph.append("\"];\n");
							}
							int start = currentClass;
							int end = previousClass;
							String edge = start + " -> " + end + " [label=\"" + previousMethod + "\"];\n";
							if (!existingEdges.contains(edge)){
								javaSubGraph.append(edge);
								existingEdges.add(edge);
							}
							previousClass = currentClass;
							previousMethod = element.getMethodName();
							previousClassName = element.getClassName();
						
						}
					}
				}
				out(builder.toString());
			}
			out("\tlabel=\"" + sink + "\";}");

		}
		out("\tsubgraph cluster_" + id++ + "{");
		out(javaSubGraph.toString());
		out("\tlabel=\"Java\";}");

		out("}");
		
	}

	
	private String abbreviate(String sql) {
		if (sql.length() > 12){
			return sql.substring(0, 12) + "...";
		}
		return sql;
	}



	private void out(String string) {
		System.out.println(string);
	}	
}
